Skip to content

fix: read version-specific h3 exports via runtime key#18

Merged
CodeDredd merged 2 commits into
mainfrom
fix/h3-export-bundling
Jun 10, 2026
Merged

fix: read version-specific h3 exports via runtime key#18
CodeDredd merged 2 commits into
mainfrom
fix/h3-export-bundling

Conversation

@CodeDredd

Copy link
Copy Markdown
Owner

Problem

1.0.0 breaks downstream Nuxt builds on h3 v1:

"toResponse" is not exported by node_modules/.pnpm/h3@1.15.11/node_modules/h3/dist/index.mjs,
imported by node_modules/.pnpm/h3-compression@1.0.0_h3@1.15.11/node_modules/h3-compression/dist/index.mjs

Root cause

helper.ts accessed the version-specific h3 symbols as namespace members — h3.toResponse (v2-only) and h3.send (v1-only). Our own dist keeps those as runtime member access, but when a downstream bundler (Nuxt → Nitro → Rollup) re-bundles dist/index.mjs, Rollup resolves h3.toResponse against the actually-installed h3@1.15.11, finds no such export, and converts it into a failing static named import. The same breaks in reverse for send under h3 v2.

Fix

Read version-specific symbols through a runtime key so no bundler can fold them into a static named import:

function h3Export<T>(name: string): T | undefined {
  return (h3 as Record<string, unknown>)[name] as T | undefined
}
const send = h3Export<...>('send')
const toResponse = h3Export<...>('toResponse')

Built output now emits h3[name] instead of h3.toResponse, so Rollup keeps import * as h3 with runtime indexing.

Test

test/dist-bundling.test.ts Rollup-bundles the built dist/index.mjs against a stub h3 that — like any single h3 major — exports neither send nor toResponse, and asserts there are no MISSING_EXPORT warnings. Verified it catches the regression: the old h3.toResponse pattern produces exactly "toResponse" is not exported by ...; the fixed pattern produces none. Version-independent, so it runs in every CI matrix cell.

🤖 Generated with Claude Code

CodeDredd and others added 2 commits June 10, 2026 07:42
`h3.toResponse` (v2-only) and `h3.send` (v1-only) were accessed as
namespace members. When a downstream bundler (Nuxt -> Nitro -> Rollup)
re-bundles dist/index.mjs, Rollup resolves those against the installed
h3 and turns them into static named imports, breaking the build for the
version that lacks the symbol (e.g. `"toResponse" is not exported by h3`
on h3 v1).

Read the symbols through a runtime key so no bundler can fold them into
a static named import. Add a Rollup-based regression test that bundles
dist/index.mjs against a stub h3 and asserts no missing-export warnings.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
pnpm's strict node_modules layout doesn't hoist rollup (a transitive
unbuild dep) to the top level, so import('rollup') fails in CI.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@CodeDredd CodeDredd merged commit ae4d7ea into main Jun 10, 2026
9 checks passed
@CodeDredd CodeDredd deleted the fix/h3-export-bundling branch June 10, 2026 05:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant